using AnalyStock.DataManage;
using static AnalyStock.DataManage.QueryCandlePointsExtension;

namespace AnalyStock.DataAnalysis;

/// <summary>
///     填充各类基础数据集合，为批量分析准备
/// </summary>

#region

internal static class FillDataCollection
{
    internal static async Task<bool> IsFilltoBasicDataAsync(string stockCode, CandlePointsParam param)
    {
        return await IsFilltoCandleSeriesAsync(stockCode, param).ConfigureAwait(false) &&
               await IsFilltoFinancialIndexAsync(stockCode).ConfigureAwait(false) &&
               await IsFilltoInflectPointsAsync().ConfigureAwait(false);
    }

    internal static async Task<IList<StockInfo>> FilltoStockInfosAsync()
    {
        try
        {
            return await CommDataHelper<StockInfo>.GetDataOnDbSetAsync().ConfigureAwait(false);
            //return stockInfos?.Where(n => !n.Name.Contains("ST")); //不含ST股,只对列表的引用
        }
        catch (Exception ex)
        {
            ErrorLogPool.LogErrorMessage("StockInfos", "all", "", ex.Message);
            return null;
        }
    }

    private static async Task<bool> IsFilltoCandleSeriesAsync(string stockCode, CandlePointsParam param)
    {
        try
        {
            var dailes = await CommDataHelper<Daily>.GetDataOnDbSetAsync(
                stockCode,
                DateTime.Today.AddMonths(-param.SelectMonthCount).ToDateString(),
                DateTime.Today.ToDateString()
            ).ConfigureAwait(false);
            //没有记录，抛出异常放弃执行
            if (dailes.IsNullOrEmpty())
            {
                throw new NullReferenceException("没有交易数据...");
            }

            if (param.IsFillCurrentDailyOnline)
            {
                dailes = await dailes.GetCurrentDailyOnlineAsync().ConfigureAwait(false);
            }

            //此处只是用接口，不进行实例化，为提高批量取数的效率
            CandlePoints?.Clear();
            CandlePoints = dailes.CreatCandlePoints(param.PeriodType);
            return !CandlePoints.IsNullOrEmpty();
        }
        catch (Exception ex)
        {
            ErrorLogPool.LogErrorMessage("Daily", stockCode, "", ex.Message);
            return false;
        }
    }

    private static async Task<bool> IsFilltoInflectPointsAsync()
    {
        SamplePoints?.Clear();
        await Task.Delay(2).ConfigureAwait(false);
        SamplePoints = CandlePoints?.CreateInflectPointsByClose(0.05f);
        return !SamplePoints.IsNullOrEmpty();
    }

    private static async Task<bool> IsFilltoFinancialIndexAsync(string stockCode)
    {
        try
        {
            FinancialIndexs?.Clear();
            FinancialIndexs = await
            CommDataHelper<FinancialIndex>.GetDataOnDbSetAsync(
                stockCode,
                DateTime.Today.AddMonths(-12).ToDateString(),
                DateTime.Today.ToDateString()
            ).ConfigureAwait(false);
            return !FinancialIndexs.IsNullOrEmpty();
        }
        catch (Exception ex)
        {
            ErrorLogPool.LogErrorMessage("Financialindex", CandlePoints[0].StockCode, "", ex.Message);
            return false;
        }
    }
}

#endregion

/// <summary>
///     生成走势中各类点的点点值，K线拐点、模拟曲线的点值、倍量的点值、财务EPS点值
/// </summary>
internal static class CreatPaintPoints
{
    // static readonly float rateWave = 0.05f;
    /// <summary>
    ///     获取价格的拐点数据，形成拐点系列值，依据价格变动的幅度：波动率
    /// </summary>
    /// <param name="candleSeries"></param>
    /// <param name="rateWave"></param>
    /// <returns></returns>
    public static IList<SamplePoint> CreateInflectPointsByClose(this IList<CandlePoint> list, float rateWave)
    {
        List<SamplePoint> inflects = new();
        if (list.Count < 2)
        {
            return inflects;
        }

        foreach (var item in
                 list.TakeCloseArray().CalcIndexOnInflectPoint(rateWave))
        {
            inflects.Add(new SamplePoint
            {
                LocationIndex = item.Key,
                StockCode = list[item.Key].StockCode,
                TradeDate = list[item.Key].Date,
                Close = list[item.Key].Close,
                FittingValue = list[item.Key].Close,
                TrendType = item.Value,
                PeriodType = "daily"
            });
        }

        return inflects;
    }

    /// <summary>
    ///     获取拐点线的趋势线，模拟趋势方程为3次函数
    /// </summary>
    /// <param name="candleSeries"></param>
    /// <param name="rateWave"></param>
    /// <returns></returns>
    public static (IList<SamplePoint> FittingPoints, double[] FittingParam)
        CreateNorderFittingPointsBySample(this IList<CandlePoint> list, int n_order)
    {
        var samples = list.FillSamplePointsByClose();
        var sampleNorders = new List<SamplePoint>();
        if (samples.Count < n_order) return (sampleNorders, null);
        //线性模型           
        var fittingParams = MathNet.Numerics.Fit.Polynomial(
                samples.Select(n => (double)n.LocationIndex).ToArray(),
                samples.Select(n => (double)n.Close).ToArray(), n_order);

        foreach (var item in samples)
        {
            var (FuncExpression, FittingValue, Derivative)
                = CommCalculate.CalcFunctionValue(fittingParams, item.LocationIndex);
            sampleNorders.Add(new SamplePoint
            {
                LocationIndex = item.LocationIndex,
                TradeDate = item.TradeDate,
                StockCode = item.StockCode,
                Close = item.Close,
                FittingValue = FittingValue,
                Derivative = Derivative,
                FuncExpression = FuncExpression,
                TrendType = "FittingFunction",
                PeriodType = "daily"
            });
        }

        samples.Dispose();
        return (sampleNorders, fittingParams);
    }

    /// <summary>
    ///     获取交易量倍量点的所有点，当日交易最低价
    /// </summary>
    /// <param name="candleSeries"></param>
    /// <returns></returns>
    public static IList<SamplePoint> CreateDoubleVolPointsByLow(this IList<CandlePoint> list)
    {
        List<SamplePoint> attacks = new();
        if (list.Count < 2)
        {
            return attacks;
        }

        foreach (var item in list.TakePoints(CandleType.DoubleVol))
        {
            attacks.Add(new SamplePoint
            {
                LocationIndex = item.LocationIndex,
                StockCode = item.StockCode,
                TradeDate = item.Date,
                TrendType = "DoubleVol",
                Close = item.Close,
                FittingValue = item.Low,
                PeriodType = "daily"
            });
        }

        return attacks;
    }

    /// <summary>
    ///     获取EPS的图像数据，及坐标索引
    /// </summary>
    /// <param name="candleSeries"></param>
    /// <returns></returns>
    public static IList<EPSPoint> CreatFinacialEPSPoints(this IList<FinancialIndex> list)
    {
        List<EPSPoint> epsPoints = new();
        if (list.IsNullOrEmpty())
        {
            return epsPoints;
        }

        foreach (var (ReportDt, EPS) in list.Select(n => (n.ReportDt, n.EPS)))
        {
            var candle = CandlePoints.Last(n => n.Month == ReportDt[..6]);
            if (candle != null)
            {
                epsPoints.Add(new EPSPoint
                {
                    LocationIndex = candle.LocationIndex,
                    ReportDate = ReportDt,
                    EPS = EPS
                });
            }
        }

        return epsPoints;
    }

    public static IList<NetAssetPSPoint> CreatNetAssetPSPoints(this IList<FinancialIndex> list)
    {
        List<NetAssetPSPoint> napsPoints = new();
        if (list.IsNullOrEmpty())
        {
            return napsPoints;
        }

        foreach (var (ReportDt, NetAssetPS) in list.Select(n => (n.ReportDt, n.NetAssetPS)))
        {
            var candle = CandlePoints.Last(n => n.Month == ReportDt[..6]);
            if (candle != null)
            {
                napsPoints.Add(new NetAssetPSPoint
                {
                    LocationIndex = candle.LocationIndex,
                    ReportDate = ReportDt,
                    NetAssetPS = NetAssetPS
                });
            }
        }

        return napsPoints;
    }

    private static IList<SamplePoint> FillSamplePointsByClose(this IList<CandlePoint> list)
    {
        List<SamplePoint> samples = new();
        if (list.Count < 5)
        {
            return samples;
        }

        var maxCloseCandle = list.MaxBy(n => n.Close);
        var minCloseCandle = list.MinBy(n => n.Close);
        var idxDivSection = list.Count / 27.0f; //3个部分，每部分9个取值点
        var indexDiv = 0;
        for (var i = 0; i <= 27; i++)
        {
            indexDiv = i * idxDivSection < 2 ? 0 : (int)(i * idxDivSection) - 1;
            samples.Add(new SamplePoint
            {
                LocationIndex = indexDiv,
                StockCode = list[indexDiv].StockCode,
                TradeDate = list[indexDiv].Date,
                Close = list[indexDiv].Close,
                PeriodType = "Daily"
            });
        }

        samples.Add(new SamplePoint
        {
            LocationIndex = maxCloseCandle.LocationIndex,
            StockCode = maxCloseCandle.StockCode,
            TradeDate = maxCloseCandle.Date,
            Close = maxCloseCandle.Close,
            PeriodType = "Daily"
        });
        samples.Add(new SamplePoint
        {
            LocationIndex = minCloseCandle.LocationIndex,
            StockCode = minCloseCandle.StockCode,
            TradeDate = minCloseCandle.Date,
            Close = minCloseCandle.Close,
            PeriodType = "Daily"
        });
        return samples.OrderBy(n => n.LocationIndex)
            .DistinctBy(n => n.LocationIndex).ToList();
    }
}

///分析中使用个数据点位值或截面值，结构类    

#region

/// <summary>
///     财务指标EPS的点位值
/// </summary>
public class EPSPoint
{
    public int LocationIndex { get; set; }
    public string ReportDate { get; set; }
    public float EPS { get; set; }
}

public class NetAssetPSPoint
{
    public int LocationIndex { get; set; }
    public string ReportDate { get; set; }
    public float NetAssetPS { get; set; }
}

/// <summary>
///     K线走势中拐点的点位值
/// </summary>
public class SamplePoint
{
    public int LocationIndex { set; get; } //坐标轴上的索引位置，不是具体索引
    public string StockCode { set; get; }
    public string TradeDate { get; set; }
    public float Close { get; set; } //收盘价
    public string PeriodType { get; set; } //期间类型
    public string TrendType { get; set; } // "↓" 上部拐点，走势方向向下了；"↑"：下部拐点，走势方向向上
    public float FittingValue { get; set; } //选取点的收盘价或模型值       
    public float Derivative { get; set; } //导数值，斜率； derivative     
    public string FuncExpression { get; set; } //函数表达式       
}

/// <summary>
///     K线走势中顶部拐点的点位值
/// </summary>
public class TopInflectPoint
{
    public int LocationIndex { get; set; }
    public float PointValue { get; set; } //该点的价格
    public float GrowToCurrent { get; set; } //该点到当前的价格增长幅度
    public bool IsSkyRocketing { get; set; } //是否涨停
    public int DaysToCurrent { get; set; } //该高点到当前的日期数
}

/// <summary>
///     取得交易区间主要的趋势各类指标
/// </summary>
public class TrendIndexInPeriod
{
    public string StockCode { set; get; } //股票码
    public string StockName { set; get; }
    public float Current { get; set; } //当前收盘价
    public float Max { get; set; } //最高收盘价
    public float Min { get; set; } //最低收盘价        
    public float WaveRate { get; set; } //收盘价振荡幅度
    public float RatioCurrentToMax { get; set; } //收盘价距最高价比例，位置
    public int MaxIdx { get; set; } //最高价的索引
    public int MinIdx { get; set; } //最低价的索引                      
    public int DaysToMax { get; set; } //当前距离最高价的交易日数
    public int DaysToMin { get; set; } //当前距离最低价的交易日数
    public int DoubleVolCountsIn20Days { get; set; } //20日内的交易量倍量数
}

/// <summary>
///     最近的财务指标值
/// </summary>
public class LastFinaIndex
{
    public float Eps { get; set; } //最近的EPS
    public float FloatMv { get; set; } //最近的流通市值
    public float TotalMv { get; set; }
}

#endregion